#include "imicodecffmpegapi_impl.h"
#include <math.h>
#include "log.h"
#include "imijpeg.hpp"
using namespace imimediacore_namespace;

imicodecffmpegapi_impl::imicodecffmpegapi_impl():
_type(0),
_width(0),
_height(0),
_h265_avcodec(0),
_h265_avframe(0),
_h264_avcodec(0),
_h264_avframe(0)
{

}

imicodecffmpegapi_impl::~imicodecffmpegapi_impl()
{

}

int imicodecffmpegapi_impl::init()
{
	av_register_all();
	return imi_retcode_ok;
}

int imicodecffmpegapi_impl::fini()
{
	_finih265lib();
	_finih264lib();
	return imi_retcode_ok;
}

int imicodecffmpegapi_impl::setdecoder(int type)
{
	_type = type;
	switch (_type)
	{
	case com_chuangmi_decoder_ImiVideoDecoder_TYPE_H265:
		{
		    LOGE("_inith265lib\n");
			_inith265lib();
		}
		break;
	case com_chuangmi_decoder_ImiVideoDecoder_TYPE_H264:
		{
		    LOGE("_inith264lib\n");
			_inith264lib();
		}
		break;
	default:
		return imi_retcode_para;
	}
	return imi_retcode_ok;
}

int imicodecffmpegapi_impl::decode(unsigned char* data, int len)
{
    unsigned char* src_data = data;
    int src_len = len;
	switch (_type)
	{
	case com_chuangmi_decoder_ImiVideoDecoder_TYPE_H265:
		{
			if (_h265_avcodec == 0)
				return imi_retcode_status;
			AVPacket avpacket;
			av_init_packet(&avpacket);
			int got_picture = 0;
			avpacket.data = src_data;
			avpacket.size = src_len;
			int len = avcodec_decode_video2(_h265_avcodec, _h265_avframe, &got_picture, &avpacket);
			av_free_packet(&avpacket);
			if(got_picture != 0)
			{
				_width = _h265_avframe->width;
				_height = _h265_avframe->height;
				LOGE("decode com_chuangmi_decoder_ImiVideoDecoder_TYPE_H265 width = %d height = %d\n", _width, _height);
			}
			else
			{
				return imi_retcode_stream_decode_fail;
			}
			return imi_retcode_ok;
		}
		break;
	case com_chuangmi_decoder_ImiVideoDecoder_TYPE_H264:
		{
			if (_h264_avcodec == 0)
			 return imi_retcode_status;
			AVPacket avpacket;
			av_init_packet(&avpacket);
			int got_picture = 0;
			avpacket.data = src_data;
			avpacket.size = src_len;
			int len = avcodec_decode_video2(_h264_avcodec, _h264_avframe, &got_picture, &avpacket);
			av_free_packet(&avpacket);
			if(got_picture != 0)
			{
				_width = _h264_avframe->width;
				_height = _h264_avframe->height;
				LOGE("decode com_chuangmi_decoder_ImiVideoDecoder_TYPE_H264 width = %d height = %d\n", _width, _height);
			}
			else
			{
				return imi_retcode_stream_decode_fail;
			}
			return imi_retcode_ok;
		}
		break;
	default:
		return imi_retcode_para;
	}
	return imi_retcode_ok;
}

int imicodecffmpegapi_impl::width()
{
	return _width;
}

int imicodecffmpegapi_impl::height()
{
	return _height;
}

int imicodecffmpegapi_impl::totexture(int y, int u, int v)
{
	return imi_retcode_notimpl;
}

int imicodecffmpegapi_impl::yuvdata(unsigned char* y, unsigned char* u, unsigned char* v)
{
	switch (_type)
	{
	case com_chuangmi_decoder_ImiVideoDecoder_TYPE_H265:
		{
		    LOGE(" TYPE_H265 _copyFrameData\n");
			_copyFrameData(_h265_avframe->data[0], _h265_avframe->linesize[0], _h265_avframe->width, _h265_avframe->height, y);
			_copyFrameData(_h265_avframe->data[1], _h265_avframe->linesize[1], _h265_avframe->width/2, _h265_avframe->height/2, u);
			_copyFrameData(_h265_avframe->data[2], _h265_avframe->linesize[2], _h265_avframe->width/2, _h265_avframe->height/2, v);
			LOGE(" _copyFrameData _width = %d _height = %d\n", _width, _height);
		}
		break;
	case com_chuangmi_decoder_ImiVideoDecoder_TYPE_H264:
		{
		    LOGE(" TYPE_H264  _copyFrameData\n");
			_copyFrameData(_h264_avframe->data[0], _h264_avframe->linesize[0], _h264_avframe->width, _h264_avframe->height, y);
			_copyFrameData(_h264_avframe->data[1], _h264_avframe->linesize[1], _h264_avframe->width/2, _h264_avframe->height/2, u);
			_copyFrameData(_h264_avframe->data[2], _h264_avframe->linesize[2], _h264_avframe->width/2, _h264_avframe->height/2, v);
			LOGE("_copyFrameData _width = %d _height = %d\n", _width, _height);
		}
		break;
	default:
		return imi_retcode_para;
	}
	return imi_retcode_ok;
}

int imicodecffmpegapi_impl::jpegdata(unsigned char* y, unsigned char* u, unsigned char* v, unsigned char* buffer, int w, int h, int q)
{
	unsigned char* ybuf = y;
	unsigned char* vbuf = u;
	unsigned char* ubuf = v;
	int jpeg_width = w;//必须16对齐 才能正常显示JPEG
	int jpeg_height = h;
	unsigned long jpeglen = 0;
	int r = YUV2Jpg(ybuf, ubuf, vbuf, jpeg_width, jpeg_height, q, jpeg_width, buffer, &jpeglen);
	return jpeglen;
}

int imicodecffmpegapi_impl::_inith265lib()
{
	AVCodec* avcodec = avcodec_find_decoder(AV_CODEC_ID_H265);
	if (avcodec == 0)
	{
		LOGE("avcodec_find_decoder AV_CODEC_ID_H265 fail\n");
		return imi_retcode_uninstall;
	}
	_h265_avcodec = avcodec_alloc_context3(avcodec);
	_h265_avcodec->frame_number = 1;
	_h265_avcodec->codec_type = AVMEDIA_TYPE_VIDEO;
	_h265_avcodec->bit_rate = 0;
	_h265_avcodec->time_base.num = 1;
	_h265_avcodec->time_base.den = 15;
	//_h265_avcodec->width = 1280;
	//_h265_avcodec->height = 720;
	int r = avcodec_open2(_h265_avcodec, avcodec, NULL);
	if(r >= 0)
	{
		_h265_avframe = av_frame_alloc();
		if(avcodec->capabilities & CODEC_CAP_TRUNCATED)
		{
			_h265_avcodec->flags |= CODEC_FLAG_TRUNCATED;
		}
	}
	else
	{
		av_free(_h265_avcodec);
		_h265_avcodec = 0;
		return r;  
	}

	return imi_retcode_ok;
}

void imicodecffmpegapi_impl::_finih265lib()
{
	if (_h265_avcodec)
	{
		avcodec_close(_h265_avcodec);
		av_free(_h265_avcodec);
		_h265_avcodec = 0;
	}
	if (_h265_avframe)
	{
		av_frame_free(&_h265_avframe);
		_h265_avframe = 0;
	}
}

int imicodecffmpegapi_impl::_inith264lib()
{
	AVCodec* avcodec = avcodec_find_decoder(AV_CODEC_ID_H264);
	if (avcodec == 0)
	{
		LOGE("avcodec_find_decoder AV_CODEC_ID_H264 fail\n");
		return imi_retcode_uninstall;
	}
	_h264_avcodec = avcodec_alloc_context3(avcodec);
	_h264_avcodec->frame_number = 1;
	_h264_avcodec->codec_type = AVMEDIA_TYPE_VIDEO;
	_h264_avcodec->bit_rate = 0;
	_h264_avcodec->time_base.num = 1;
	_h264_avcodec->time_base.den = 15;
	//_h264_avcodec->width = 1280;
	//_h264_avcodec->height = 720;
	int r = avcodec_open2(_h264_avcodec, avcodec, NULL);
	if(r >= 0)
	{
		_h264_avframe = av_frame_alloc();
		if(avcodec->capabilities & CODEC_CAP_TRUNCATED)
		{
			_h264_avcodec->flags |= CODEC_FLAG_TRUNCATED;
		}
	}
	else
	{
		av_free(_h264_avcodec);
		_h264_avcodec = 0;
		return r;
	}
	return imi_retcode_ok;
}

void imicodecffmpegapi_impl::_finih264lib()
{
	if (_h264_avcodec)
	{
		avcodec_close(_h264_avcodec);
		av_free(_h264_avcodec);
		_h264_avcodec = 0;
	}
	if (_h264_avframe)
	{
		av_frame_free(&_h264_avframe);
		_h264_avframe = 0;
	}
}

void imicodecffmpegapi_impl::_copyFrameData(unsigned char* data, int linesize, int width, int height,  unsigned char*out)
{
	if (linesize == width)
	{
		memcpy(out, data, width*height);
	}
	else
	{
		int w = min(linesize, width);
		unsigned char* src = data;
		unsigned char* dst = out;
		for (unsigned int i = 0; i < height; i++)
		{
			memcpy(dst, src, w);
			dst += w;
			src += linesize;
		}
	}
}